import axios, { AxiosError, type AxiosRequestConfig, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios'
import { ElMessageBox, ElNotification } from 'element-plus'
import appConfig from '@/config/index'

import { clearLocalStorage, getRTExp, getToken, setToken } from './cache'
import { updateToken, type LoginResult } from '@/api/user'

let isRefreshing = false
let retryReqs: any[] = []

const requestTokenInterceptor = (config: InternalAxiosRequestConfig) => {
  const token = getToken()
  if (token && !config.headers?.Authorization) {
    config.headers!.Authorization = token
  }
  return config
}
const resSuccessInterceptor = (response: AxiosResponse) => {
  const res = response?.data
  // res 有值
  if (res || response.config?.responseType === 'blob') {
    if (res.code !== 200) {
      return new Error(res.msg)
    }
    return res
  }
  return null
}
const resErrorInterceptor = async (error: AxiosError<any>) => {
  const response = error.response
  const config = response?.config as AxiosRequestConfig
  if (response?.status === 401) {
    if (getRTExp() <= Date.now()) {
      // 刷新token 过期了
      ElMessageBox.alert('您的登录已过期，点击跳转登录', '提示', {
        confirmButtonText: 'OK',
        callback: () => {
          clearLocalStorage()
          window.location.reload()
        }
      })
    } else if (!isRefreshing) {
      try {
        isRefreshing = true
        const res = await updateToken()
        if (res?.code === 200) {
          const data = res.data as LoginResult
          setToken(data.accessToken, data.refreshToken)
          // 队列中的请求刷新成功后，再请求一次
          for (let i = 0, len = retryReqs.length; i < len; i++) {
            retryReqs[i](data.accessToken)
          }
          // 队列请求完成，清空
          retryReqs = []
          // 返回触发 401 接口正常结果
          config.headers = { ...config.headers, Authorization: data.accessToken }
          return await request(config)
        }
      } catch (error) {
        console.log(error)
      } finally {
        isRefreshing = false
      }
    } else {
      // 刷新 token 期间，将其他请求存入队列，刷新成功之后重新请求一次
      return new Promise((resolve) => {
        retryReqs.push((token: string) => {
          config.headers = { ...config.headers, Authorization: token }
          resolve(request(config))
        })
      })
    }
  } else {
    console.log('出错了', response?.data?.msg)
    const showMsg =
      response?.data?.msg === 'Client Error' ? (response?.data.error || []).join(';') : response?.data?.msg
    ElNotification({
      title: '服务端错误',
      dangerouslyUseHTMLString: true,
      message: `<div style="color: var(--el-color-error, red);">${showMsg || error.message}</div>`,
      position: 'bottom-right',
      duration: 3000
    })
  }
}
function createService() {
  const service = axios.create()

  service.interceptors.request.use(requestTokenInterceptor)

  service.interceptors.response.use(resSuccessInterceptor, resErrorInterceptor)

  return service
}

async function request<T>(config: AxiosRequestConfig): Promise<T> {
  const defaultConfig = {
    timeout: Number(appConfig.request.timeout || '0'),
    baseURL: appConfig.api.baseUrl
  }
  const service = createService()
  return new Promise<T>((resolve, reject) => {
    setTimeout(async () => {
      await service({ ...defaultConfig, ...config })
        .then((res) => {
          if (res) {
            resolve(res as T)
          } else {
            reject(new Error('未知异常'))
          }
        })
        .catch((e) => {
          console.log(e)
          reject(e)
        })
    }, 0)
  })
}

export default request
